/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note
 * Copyright (C) 2016-2018, LomboTech Co.Ltd.
 * Author: lomboswer <lomboswer@lombotech.com>
 *
 * gc2145 mipi-csi sensor driver code for LomboTech
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 */

#include <linux/init.h>
#include <linux/module.h>

#include <linux/slab.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/viss_isp.h>

#include <media/lombo_viss.h>

#include <mach/debug.h>
#include "../ccc.h"

#define GC2145_800x600_WIDTH		800
#define GC2145_800x600_HEIGHT		600

#define GC2145_1600x1200_WIDTH		1600
#define GC2145_1600x1200_HEIGHT		1200

#define GC2145MIPI_2Lane

/*
 * 2lane YUV init
 */
static const cc_reg_t init_reg_list[] = {
	{0xfe, 0xf0},
	{0xfe, 0xf0},
	{0xfe, 0xf0},
	{0xfc, 0x06},
	{0xf6, 0x00},
	{0xf7, 0x1d},
	{0xf8, 0x84},
	{0xfa, 0x00},
	{0xf9, 0x8e},
	{0xf2, 0x00},
	/* ISP reg */
	{0xfe, 0x00},
	{0x03, 0x04},
	{0x04, 0xe2},
	{0x09, 0x00},
	{0x0a, 0x00},
	{0x0b, 0x00},
	{0x0c, 0x00},
	{0x0d, 0x04},
	{0x0e, 0xc0},
	{0x0f, 0x06},
	{0x10, 0x52},
	{0x12, 0x2e},
	{0x17, 0x14}, /* mirror */
	{0x18, 0x22},
	{0x19, 0x0e},
	{0x1a, 0x01},
	{0x1b, 0x4b},
	{0x1c, 0x07},
	{0x1d, 0x10},
	{0x1e, 0x88},
	{0x1f, 0x78},
	{0x20, 0x03},
	{0x21, 0x40},
	{0x22, 0xdc},
	{0x24, 0x16},
	{0x25, 0x01},
	{0x26, 0x10},
	{0x2d, 0x60},
	{0x30, 0x01},
	{0x31, 0x90},
	{0x33, 0x06},
	{0x34, 0x01},

	{0xfe, 0x00},
	{0x80, 0x7f},
	{0x81, 0x26},
	{0x82, 0xfa},
	{0x83, 0x02},
	{0x84, 0x03},
	{0x86, 0x02},
	{0x88, 0x03},
	{0x89, 0x03},
	{0x85, 0x08},
	{0x8a, 0x00},
	{0x8b, 0x00},
	{0xb0, 0x55},
	{0xc3, 0x00},
	{0xc4, 0x80},
	{0xc5, 0x90},
	{0xc6, 0x3b},
	{0xc7, 0x46},
	{0xec, 0x06},
	{0xed, 0x04},
	{0xee, 0x60},
	{0xef, 0x90},
	{0xb6, 0x01},
	{0x90, 0x01},
	{0x91, 0x00},
	{0x92, 0x00},
	{0x93, 0x00},
	{0x94, 0x00},
	{0x95, 0x04},
	{0x96, 0xb0},
	{0x97, 0x06},
	{0x98, 0x40},
	/*  BLK */
	{0xfe, 0x00},
	{0x40, 0x42},
	{0x41, 0x00},
	{0x43, 0x5b},
	{0x5e, 0x00},
	{0x5f, 0x00},
	{0x60, 0x00},
	{0x61, 0x00},
	{0x62, 0x00},
	{0x63, 0x00},
	{0x64, 0x00},
	{0x65, 0x00},
	{0x66, 0x20},
	{0x67, 0x20},
	{0x68, 0x20},
	{0x69, 0x20},
	{0x76, 0x00},
	{0x6a, 0x08},
	{0x6b, 0x08},
	{0x6c, 0x08},
	{0x6d, 0x08},
	{0x6e, 0x08},
	{0x6f, 0x08},
	{0x70, 0x08},
	{0x71, 0x08},
	{0x76, 0x00},
	{0x72, 0xf0},
	{0x7e, 0x3c},
	{0x7f, 0x00},
	{0xfe, 0x02},
	{0x48, 0x15},
	{0x49, 0x00},
	{0x4b, 0x0b},
	{0xfe, 0x00},
	/* AEC */
	{0xfe, 0x01},
	{0x01, 0x04},
	{0x02, 0xc0},
	{0x03, 0x04},
	{0x04, 0x90},
	{0x05, 0x24},
	{0x06, 0x80},
	{0x07, 0x1b},
	{0x08, 0x60},
	{0x09, 0x00},
	{0x0a, 0x82},
	{0x0b, 0x11},
	{0x0c, 0x10},
	{0x11, 0x10},
	{0x13, 0x50},
	{0x17, 0x00},
	{0x1c, 0x11},
	{0x1e, 0x61},
	{0x1f, 0x35},
	{0x20, 0x40},
	{0x22, 0x40},
	{0x23, 0x20},
	{0xfe, 0x02},
	{0x0f, 0x04},
	{0xfe, 0x01},
	{0x12, 0x35},
	{0x15, 0x50},
	{0x10, 0x31},
	{0x3e, 0x28},
	{0x3f, 0xb0},
	{0x40, 0x90},
	{0x41, 0x0f},
	/* INTPEE */
	{0xfe, 0x02},
	{0x90, 0x6c},
	{0x91, 0x03},
	{0x92, 0xcb},
	{0x94, 0x33},
	{0x95, 0x84},
	{0x97, 0x65},
	{0xa2, 0x11},
	{0xfe, 0x00},
	/* DNDD */
	{0xfe, 0x02},
	{0x80, 0xc1},
	{0x81, 0x08},
	{0x82, 0x05},
	{0x83, 0x08},
	{0x84, 0x0a},
	{0x86, 0xf0},
	{0x87, 0x50},
	{0x88, 0x15},
	{0x89, 0xb0},
	{0x8a, 0x30},
	{0x8b, 0x10},
	/* ASDE */
	{0xfe, 0x01},
	{0x21, 0x04},
	{0xfe, 0x02},
	{0xa3, 0x50},
	{0xa4, 0x20},
	{0xa5, 0x40},
	{0xa6, 0x80},
	{0xab, 0x40},
	{0xae, 0x0c},
	{0xb3, 0x46},
	{0xb4, 0x64},
	{0xb6, 0x38},
	{0xb7, 0x01},
	{0xb9, 0x2b},
	{0x3c, 0x04},
	{0x3d, 0x15},
	{0x4b, 0x06},
	{0x4c, 0x20},
	{0xfe, 0x00},
	/* GAMMA */

	/* gamma1 */
#if CCC_USED
	{0xfe, 0x02},
	{0x10, 0x09},
	{0x11, 0x0d},
	{0x12, 0x13},
	{0x13, 0x19},
	{0x14, 0x27},
	{0x15, 0x37},
	{0x16, 0x45},
	{0x17, 0x53},
	{0x18, 0x69},
	{0x19, 0x7d},
	{0x1a, 0x8f},
	{0x1b, 0x9d},
	{0x1c, 0xa9},
	{0x1d, 0xbd},
	{0x1e, 0xcd},
	{0x1f, 0xd9},
	{0x20, 0xe3},
	{0x21, 0xea},
	{0x22, 0xef},
	{0x23, 0xf5},
	{0x24, 0xf9},
	{0x25, 0xff},
#else
	{0xfe, 0x02},
	{0x10, 0x0a},
	{0x11, 0x12},
	{0x12, 0x19},
	{0x13, 0x1f},
	{0x14, 0x2c},
	{0x15, 0x38},
	{0x16, 0x42},
	{0x17, 0x4e},
	{0x18, 0x63},
	{0x19, 0x76},
	{0x1a, 0x87},
	{0x1b, 0x96},
	{0x1c, 0xa2},
	{0x1d, 0xb8},
	{0x1e, 0xcb},
	{0x1f, 0xd8},
	{0x20, 0xe2},
	{0x21, 0xe9},
	{0x22, 0xf0},
	{0x23, 0xf8},
	{0x24, 0xfd},
	{0x25, 0xff},
	{0xfe, 0x00},
#endif
	{0xfe, 0x00},
	{0xc6, 0x20},
	{0xc7, 0x2b},
	/* gamma2 */
#if CCC_USED
	{0xfe, 0x02},
	{0x26, 0x0f},
	{0x27, 0x14},
	{0x28, 0x19},
	{0x29, 0x1e},
	{0x2a, 0x27},
	{0x2b, 0x33},
	{0x2c, 0x3b},
	{0x2d, 0x45},
	{0x2e, 0x59},
	{0x2f, 0x69},
	{0x30, 0x7c},
	{0x31, 0x89},
	{0x32, 0x98},
	{0x33, 0xae},
	{0x34, 0xc0},
	{0x35, 0xcf},
	{0x36, 0xda},
	{0x37, 0xe2},
	{0x38, 0xe9},
	{0x39, 0xf3},
	{0x3a, 0xf9},
	{0x3b, 0xff},
#else
	/* Gamma outdoor */
	{0xfe, 0x02},
	{0x26, 0x17},
	{0x27, 0x18},
	{0x28, 0x1c},
	{0x29, 0x20},
	{0x2a, 0x28},
	{0x2b, 0x34},
	{0x2c, 0x40},
	{0x2d, 0x49},
	{0x2e, 0x5b},
	{0x2f, 0x6d},
	{0x30, 0x7d},
	{0x31, 0x89},
	{0x32, 0x97},
	{0x33, 0xac},
	{0x34, 0xc0},
	{0x35, 0xcf},
	{0x36, 0xda},
	{0x37, 0xe5},
	{0x38, 0xec},
	{0x39, 0xf8},
	{0x3a, 0xfd},
	{0x3b, 0xff},
#endif
	/* YCP */
	{0xfe, 0x02},
	{0xd1, 0x32},
	{0xd2, 0x32},
	{0xd3, 0x40},
	{0xd6, 0xf0},
	{0xd7, 0x10},
	{0xd8, 0xda},
	{0xdd, 0x14},
	{0xde, 0x86},
	{0xed, 0x80},
	{0xee, 0x00},
	{0xef, 0x3f},
	{0xd8, 0xd8},
	/* abs */
	{0xfe, 0x01},
	{0x9f, 0x40},
	/* LSC */
	{0xfe, 0x01},
	{0xc2, 0x14},
	{0xc3, 0x0d},
	{0xc4, 0x0c},
	{0xc8, 0x15},
	{0xc9, 0x0d},
	{0xca, 0x0a},
	{0xbc, 0x24},
	{0xbd, 0x10},
	{0xbe, 0x0b},
	{0xb6, 0x25},
	{0xb7, 0x16},
	{0xb8, 0x15},
	{0xc5, 0x00},
	{0xc6, 0x00},
	{0xc7, 0x00},
	{0xcb, 0x00},
	{0xcc, 0x00},
	{0xcd, 0x00},
	{0xbf, 0x07},
	{0xc0, 0x00},
	{0xc1, 0x00},
	{0xb9, 0x00},
	{0xba, 0x00},
	{0xbb, 0x00},
	{0xaa, 0x01},
	{0xab, 0x01},
	{0xac, 0x00},
	{0xad, 0x05},
	{0xae, 0x06},
	{0xaf, 0x0e},
	{0xb0, 0x0b},
	{0xb1, 0x07},
	{0xb2, 0x06},
	{0xb3, 0x17},
	{0xb4, 0x0e},
	{0xb5, 0x0e},
	{0xd0, 0x09},
	{0xd1, 0x00},
	{0xd2, 0x00},
	{0xd6, 0x08},
	{0xd7, 0x00},
	{0xd8, 0x00},
	{0xd9, 0x00},
	{0xda, 0x00},
	{0xdb, 0x00},
	{0xd3, 0x0a},
	{0xd4, 0x00},
	{0xd5, 0x00},
	{0xa4, 0x00},
	{0xa5, 0x00},
	{0xa6, 0x77},
	{0xa7, 0x77},
	{0xa8, 0x77},
	{0xa9, 0x77},
	{0xa1, 0x80},
	{0xa2, 0x80},
	{0xfe, 0x01},
	{0xdf, 0x0d},
	{0xdc, 0x25},
	{0xdd, 0x30},
	{0xe0, 0x77},
	{0xe1, 0x80},
	{0xe2, 0x77},
	{0xe3, 0x90},
	{0xe6, 0x90},
	{0xe7, 0xa0},
	{0xe8, 0x90},
	{0xe9, 0xa0},
	{0xfe, 0x00},
	/* AWB */
	{0xfe, 0x01},
	{0x4f, 0x00},
	{0x4f, 0x00},
	{0x4b, 0x01},
	{0x4f, 0x00},
	{0x4c, 0x01}, /* D75 */
	{0x4d, 0x71},
	{0x4e, 0x01},
	{0x4c, 0x01},
	{0x4d, 0x91},
	{0x4e, 0x01},
	{0x4c, 0x01},
	{0x4d, 0x70},
	{0x4e, 0x01},
	{0x4c, 0x01}, /* D65 */
	{0x4d, 0x90},
	{0x4e, 0x02},
	{0x4c, 0x01},
	{0x4d, 0xb0},
	{0x4e, 0x02},
	{0x4c, 0x01},
	{0x4d, 0x8f},
	{0x4e, 0x02},
	{0x4c, 0x01},
	{0x4d, 0x6f},
	{0x4e, 0x02},
	{0x4c, 0x01},
	{0x4d, 0xaf},
	{0x4e, 0x02},
	{0x4c, 0x01},
	{0x4d, 0xd0},
	{0x4e, 0x02},
	{0x4c, 0x01},
	{0x4d, 0xf0},
	{0x4e, 0x02},
	{0x4c, 0x01},
	{0x4d, 0xcf},
	{0x4e, 0x02},
	{0x4c, 0x01},
	{0x4d, 0xef},
	{0x4e, 0x02},
	{0x4c, 0x01},/* D50 */
	{0x4d, 0x6e},
	{0x4e, 0x03},
	{0x4c, 0x01},
	{0x4d, 0x8e},
	{0x4e, 0x03},
	{0x4c, 0x01},
	{0x4d, 0xae},
	{0x4e, 0x03},
	{0x4c, 0x01},
	{0x4d, 0xce},
	{0x4e, 0x03},
	{0x4c, 0x01},
	{0x4d, 0x4d},
	{0x4e, 0x03},
	{0x4c, 0x01},
	{0x4d, 0x6d},
	{0x4e, 0x03},
	{0x4c, 0x01},
	{0x4d, 0x8d},
	{0x4e, 0x03},
	{0x4c, 0x01},
	{0x4d, 0xad},
	{0x4e, 0x03},
	{0x4c, 0x01},
	{0x4d, 0xcd},
	{0x4e, 0x03},
	{0x4c, 0x01},
	{0x4d, 0x4c},
	{0x4e, 0x03},
	{0x4c, 0x01},
	{0x4d, 0x6c},
	{0x4e, 0x03},
	{0x4c, 0x01},
	{0x4d, 0x8c},
	{0x4e, 0x03},
	{0x4c, 0x01},
	{0x4d, 0xac},
	{0x4e, 0x03},
	{0x4c, 0x01},
	{0x4d, 0xcc},
	{0x4e, 0x03},
	{0x4c, 0x01},
	{0x4d, 0xcb},
	{0x4e, 0x03},
	{0x4c, 0x01},
	{0x4d, 0x4b},
	{0x4e, 0x03},
	{0x4c, 0x01},
	{0x4d, 0x6b},
	{0x4e, 0x03},
	{0x4c, 0x01},
	{0x4d, 0x8b},
	{0x4e, 0x03},
	{0x4c, 0x01},
	{0x4d, 0xab},
	{0x4e, 0x03},
	{0x4c, 0x01},/* CWF */
	{0x4d, 0x8a},
	{0x4e, 0x04},
	{0x4c, 0x01},
	{0x4d, 0xaa},
	{0x4e, 0x04},
	{0x4c, 0x01},
	{0x4d, 0xca},
	{0x4e, 0x04},
	{0x4c, 0x01},
	{0x4d, 0xca},
	{0x4e, 0x04},
	{0x4c, 0x01},
	{0x4d, 0xc9},
	{0x4e, 0x04},
	{0x4c, 0x01},
	{0x4d, 0x8a},
	{0x4e, 0x04},
	{0x4c, 0x01},
	{0x4d, 0x89},
	{0x4e, 0x04},
	{0x4c, 0x01},
	{0x4d, 0xa9},
	{0x4e, 0x04},
	{0x4c, 0x02},/* tl84 */
	{0x4d, 0x0b},
	{0x4e, 0x05},
	{0x4c, 0x02},
	{0x4d, 0x0a},
	{0x4e, 0x05},
	{0x4c, 0x01},
	{0x4d, 0xeb},
	{0x4e, 0x05},
	{0x4c, 0x01},
	{0x4d, 0xea},
	{0x4e, 0x05},
	{0x4c, 0x02},
	{0x4d, 0x09},
	{0x4e, 0x05},
	{0x4c, 0x02},
	{0x4d, 0x29},
	{0x4e, 0x05},
	{0x4c, 0x02},
	{0x4d, 0x2a},
	{0x4e, 0x05},
	{0x4c, 0x02},
	{0x4d, 0x4a},
	{0x4e, 0x05},
	/* A */
	/* {0x4c , 0x02}, */
	/* {0x4d , 0x6a}, */
	/* {0x4e , 0x06}, */
	{0x4c, 0x02},
	{0x4d, 0x8a},
	{0x4e, 0x06},
	{0x4c, 0x02},
	{0x4d, 0x49},
	{0x4e, 0x06},
	{0x4c, 0x02},
	{0x4d, 0x69},
	{0x4e, 0x06},
	{0x4c, 0x02},
	{0x4d, 0x89},
	{0x4e, 0x06},
	{0x4c, 0x02},
	{0x4d, 0xa9},
	{0x4e, 0x06},
	{0x4c, 0x02},
	{0x4d, 0x48},
	{0x4e, 0x06},
	{0x4c, 0x02},
	{0x4d, 0x68},
	{0x4e, 0x06},
	{0x4c, 0x02},
	{0x4d, 0x69},
	{0x4e, 0x06},
	{0x4c, 0x02},/* H */
	{0x4d, 0xca},
	{0x4e, 0x07},
	{0x4c, 0x02},
	{0x4d, 0xc9},
	{0x4e, 0x07},
	{0x4c, 0x02},
	{0x4d, 0xe9},
	{0x4e, 0x07},
	{0x4c, 0x03},
	{0x4d, 0x09},
	{0x4e, 0x07},
	{0x4c, 0x02},
	{0x4d, 0xc8},
	{0x4e, 0x07},
	{0x4c, 0x02},
	{0x4d, 0xe8},
	{0x4e, 0x07},
	{0x4c, 0x02},
	{0x4d, 0xa7},
	{0x4e, 0x07},
	{0x4c, 0x02},
	{0x4d, 0xc7},
	{0x4e, 0x07},
	{0x4c, 0x02},
	{0x4d, 0xe7},
	{0x4e, 0x07},
	{0x4c, 0x03},
	{0x4d, 0x07},
	{0x4e, 0x07},
	{0x4f, 0x01},
	{0x50, 0x80},
	{0x51, 0xa8},
	{0x52, 0x47},
	{0x53, 0x38},
	{0x54, 0xc7},
	{0x56, 0x0e},
	{0x58, 0x08},
	{0x5b, 0x00},
	{0x5c, 0x74},
	{0x5d, 0x8b},
	{0x61, 0xdb},
	{0x62, 0xb8},
	{0x63, 0x86},
	{0x64, 0xc0},
	{0x65, 0x04},
	{0x67, 0xa8},
	{0x68, 0xb0},
	{0x69, 0x00},
	{0x6a, 0xa8},
	{0x6b, 0xb0},
	{0x6c, 0xaf},
	{0x6d, 0x8b},
	{0x6e, 0x50},
	{0x6f, 0x18},
	{0x73, 0xf0},
	{0x70, 0x0d},
	{0x71, 0x60},
	{0x72, 0x80},
	{0x74, 0x01},
	{0x75, 0x01},
	{0x7f, 0x0c},
	{0x76, 0x70},
	{0x77, 0x58},
	{0x78, 0xa0},
	{0x79, 0x5e},
	{0x7a, 0x54},
	{0x7b, 0x58},
	{0xfe, 0x00},
	/* CC */
	{0xfe, 0x02},
	{0xc0, 0x01},
	{0xc1, 0x44},
	{0xc2, 0xfd},
	{0xc3, 0x04},
	{0xc4, 0xf0},
	{0xc5, 0x48},
	{0xc6, 0xfd},
	{0xc7, 0x46},
	{0xc8, 0xfd},
	{0xc9, 0x02},
	{0xca, 0xe0},
	{0xcb, 0x45},
	{0xcc, 0xec},
	{0xcd, 0x48},
	{0xce, 0xf0},
	{0xcf, 0xf0},
	{0xe3, 0x0c},
	{0xe4, 0x4b},
	{0xe5, 0xe0},
	/* ABS */
	{0xfe, 0x01},
	{0x9f, 0x40},
	{0xfe, 0x00},
	/* OUTPUT */
	{0xfe, 0x00},
	{0xf2, 0x0f},  /* 0x00 */
	/* frame rate 50Hz */
	{0xfe, 0x00},
	{0x05, 0x01},
	{0x06, 0x56},
	{0x07, 0x02},  /* 0x00 */
	{0x08, 0x06},  /* 0x32 */
	{0xfe, 0x01},
	{0x25, 0x00},
	{0x26, 0xfa},
	{0x27, 0x04},
	{0x28, 0xe2}, /* 20fps */
#if CCC_NO_USE
	{0x29, 0x06},
	{0x2a, 0xd6}, /* 14fps */
	{0x2b, 0x07},
	{0x2c, 0xd0}, /* 12fps */
	{0x2d, 0x0b},
	{0x2e, 0xb8}, /* 8fps */
#elif CCC_USED
	{0x29, 0x05},
	{0x2a, 0xdc}, /* 14fps */
	{0x2b, 0x06},
	{0x2c, 0xd6}, /* 12fps */
	{0x2d, 0x0b},
	{0x2e, 0xb8}, /* 8fps */
#endif
	{0xfe, 0x00},
	/* dark sun */
	{0xfe, 0x02},
	{0x40, 0xbf},
	{0x46, 0xcf},
	{0xfe, 0x00},
	/* MIPI */
	{0xfe, 0x03},
	{0x02, 0x22},
	{0x03, 0x10}, /* 0x12 20140821 */
	{0x04, 0x10}, /* 0x01 */
	{0x05, 0x00},
	{0x06, 0x88},
#ifdef GC2145MIPI_2Lane
	{0x01, 0x87},
	{0x10, 0x95},
#else
	{0x01, 0x83},
	{0x10, 0x94},
#endif
	{0x11, 0x1e},
	{0x12, 0x80},
	{0x13, 0x0c},
	{0x15, 0x10},
	{0x17, 0xf0},
	{0x21, 0x10},
	{0x22, 0x04},
	{0x23, 0x10},
	{0x24, 0x10},
	{0x25, 0x10},
	{0x26, 0x05},
	{0x29, 0x03},
	{0x2a, 0x0a},
	{0x2b, 0x06},
	{0xfe, 0x00},
};


/*
 * 800x600
 * Input Clock = 24Mhz bit rate 480Mbps
 */
static const cc_reg_t gc2145_yuv_800x600[] = {
	{0xfe, 0x00},
#if CCC_NO_USE
	{0xfd, 0x01},
	{0xfa, 0x00},
	/* crop window */
	{0xfe, 0x00},
	{0x90, 0x01},
	{0x91, 0x00},
	{0x92, 0x00},
	{0x93, 0x00},
	{0x94, 0x00},
	{0x95, 0x02},
	{0x96, 0x58},
	{0x97, 0x03},
	{0x98, 0x20},
	{0x99, 0x11},
	{0x9a, 0x06},
#elif CCC_USED
	{0xfa, 0x00},
	{0xfd, 0x01},
	/* crop window */
	{0xfe, 0x00},
	{0x99, 0x11},
	{0x9a, 0x06},
	{0x9b, 0x00},
	{0x9c, 0x00},
	{0x9d, 0x00},
	{0x9e, 0x00},
	{0x9f, 0x00},
	{0xa0, 0x00},
	{0xa1, 0x00},
	{0xa2, 0x00},
	{0x90, 0x01},
	{0x91, 0x00},
	{0x92, 0x00},
	{0x93, 0x00},
	{0x94, 0x00},
	{0x95, 0x02},
	{0x96, 0x58},
	{0x97, 0x03},
	{0x98, 0x20},
#endif

	/* AWB */
	{0xfe, 0x00},
	{0xec, 0x02},
	{0xed, 0x02},
	{0xee, 0x30},
	{0xef, 0x48},
	{0xfe, 0x02},
	{0x9d, 0x08},
	{0xfe, 0x01},
	{0x74, 0x00},
	/* AEC */
	{0xfe, 0x01},
	{0x01, 0x04},
	{0x02, 0x60},
	{0x03, 0x02},
	{0x04, 0x48},
	{0x05, 0x18},
	{0x06, 0x50},
	{0x07, 0x10},
	{0x08, 0x38},
	{0x0a, 0x80},
	{0x21, 0x04},
	{0xfe, 0x00},
	{0x20, 0x03},
	/* mipi */
	{0xfe, 0x03},
	{0x12, 0x40},
	{0x13, 0x06},
#if defined(GC2145MIPI_2Lane)
	{0x04, 0x90},
	{0x05, 0x01},
#else
	{0x04, 0x01},
	{0x05, 0x00},
#endif
	{0xfe, 0x00},
	{0xfe, 0x00},
};

static const cc_reg_t gc2145_yuv_1600x1200[] = {
	{0xfe, 0x00},
#if CCC_NO_USE
	{0xfa, 0x11},
#elif CCC_USED
	{0xfa, 0x00},
#endif
	{0xfd, 0x00},

	/* crop window */
	{0xfe, 0x00},
	{0x99, 0x11},
	{0x9a, 0x06},
	{0x9b, 0x00},
	{0x9c, 0x00},
	{0x9d, 0x00},
	{0x9e, 0x00},
	{0x9f, 0x00},
	{0xa0, 0x00},
	{0xa1, 0x00},
	{0xa2, 0x00},
	{0x90, 0x01},
	{0x91, 0x00},
	{0x92, 0x00},
	{0x93, 0x00},
	{0x94, 0x00},
	{0x95, 0x04},
	{0x96, 0xb0},
	{0x97, 0x06},
	{0x98, 0x40},

	/* AWB */
	{0xfe, 0x00},
	{0xec, 0x02},
	{0xed, 0x04},
	{0xee, 0x60},
	{0xef, 0x90},
	{0xfe, 0x01},
	{0x74, 0x01},
	/* AEC */
	{0xfe, 0x01},
	{0x01, 0x08},
	{0x02, 0xc0},
	{0x03, 0x04},
	{0x04, 0x90},
	{0x05, 0x30},
	{0x06, 0x98},
	{0x07, 0x28},
	{0x08, 0x6c},
	{0x0a, 0xc2},
#if CCC_NO_USE
	{0x21, 0x15}, /* if 0xfa=11,then 0x21=15;else if 0xfa=00,then 0x21=14 */
#elif CCC_USED
	{0x21, 0x14}, /* if 0xfa=11,then 0x21=15;else if 0xfa=00,then 0x21=14 */
#endif
	{0xfe, 0x00},

	/* mipi */
	{0xfe, 0x03},
	{0x12, 0x80},
	{0x13, 0x0c},
#if defined(GC2145MIPI_2Lane)
	{0x04, 0x90},
	{0x05, 0x01},
#else
	{0x04, 0x01},
	{0x05, 0x00},
#endif
	{0xfe, 0x00},
	{0xfe, 0x00},
};

static struct cc_power_act power_up_act[] = {
	{CC_PWDN,  CC_UP},
	{CC_DELAY, 5},
	{CC_RESET, CC_DOWN},
	{CC_DELAY, 5},
	{CC_PWDN, CC_DOWN},
	{CC_DELAY, 5},
	{CC_RESET,  CC_UP},
	{CC_DELAY, 5},
};

static struct cc_power_act power_down_act[] = {
	{CC_RESET, CC_DOWN},
	{CC_DELAY, 10},
	{CC_PWDN,  CC_UP},
};

static const struct cc_frame_cfg frame_cfgs[] = {
	{
		.width = GC2145_800x600_WIDTH,
		.height = GC2145_800x600_HEIGHT,
		.framerate = { 1000, 14277 },	/* 14.277fps */
		.mfreq = 480000000,	/* mipi-csi clock */
		.reg_cfgs[0].regs = gc2145_yuv_800x600,
		.reg_cfgs[0].regs_cnt = ARRAY_SIZE(gc2145_yuv_800x600),

#ifdef GC2145MIPI_2Lane
		.dlanes = 2,
#else
		.dlanes = 1,
#endif

		.mbus_code = MEDIA_BUS_FMT_YUYV8_2X8,
		.colorspace = V4L2_COLORSPACE_JPEG,
		.field = V4L2_FIELD_NONE,
	},
	{
		.width = GC2145_1600x1200_WIDTH,
		.height = GC2145_1600x1200_HEIGHT,
		.framerate = { 1000, 14277 },	/* 14.277fps */
		.mfreq = 960000000,	/* mipi-csi clock */
		.reg_cfgs[0].regs = gc2145_yuv_1600x1200,
		.reg_cfgs[0].regs_cnt = ARRAY_SIZE(gc2145_yuv_1600x1200),

#ifdef GC2145MIPI_2Lane
		.dlanes = 2,
#else
		.dlanes = 1,
#endif

		.mbus_code = MEDIA_BUS_FMT_YUYV8_2X8,
		.colorspace = V4L2_COLORSPACE_JPEG,
		.field = V4L2_FIELD_NONE,
	},
};

static int sensor_detect(struct cc_sensor_info *cc_si)
{
	u8 tmp[2];
	int ret = 0;
	u16 id;

	ret = cc_si->read_reg(cc_si, 0xF0, &tmp[0]);
	if (ret != 0) {
		PRT_ERR("tmp[0]=%x\n", tmp[0]);
		return -ENODEV;
	}
	ret = cc_si->read_reg(cc_si, 0xF1, &tmp[1]);
	if (ret != 0) {
		PRT_ERR("tmp[0]=%x,tmp[1]=%x\n", tmp[0], tmp[1]);
		return -ENODEV;
	}
	id = (tmp[0] << 8) | tmp[1];
	PRT_INFO("id: 0x%04x.\n", id);

	if (id != 0x2145) {
		PRT_ERR("ID wrong! (0x%04x != 0x2145)\n", id);
		return -ENODEV;
	}

	return 0;
}

static int sensor_init(struct cc_sensor_info *cc_si)
{
	struct timespec ts_s;
	struct timespec ts_f;
	int ret = 0;

	PRT_INFO("\n");

	ret = sensor_detect(cc_si);
	if (ret != 0)
		return ret;

	ktime_get_ts(&ts_s);
	ret = cc_si->write_reg_list(cc_si, init_reg_list,
					ARRAY_SIZE(init_reg_list));
	if (ret != 0) {
		PRT_ERR("write %s init_reg_list failed!\n", cc_si->name);
		return -ENODEV;
	}
	ktime_get_ts(&ts_f);

	PRT_INFO("write %s init_reg_list finish.\n", cc_si->name);
	PRT_INFO("timeused %d %d\n", (int)(ts_f.tv_sec - ts_s.tv_sec),
		(int)(ts_f.tv_nsec - ts_s.tv_nsec));

	if (cc_si->vflip && cc_si->mirror) {
		PRT_DBG("cc_si->vflip && cc_si->mirror\n");
		ret = cc_si->write_reg(cc_si, 0x17, 0x17);
		if (ret != 0) {
			PRT_ERR("set mirror failed ret: %d.\n", ret);
			return ret;
		}
	} else if (cc_si->vflip) {
		PRT_DBG("cc_si->vflip\n");
		ret = cc_si->write_reg(cc_si, 0x17, 0x16);
		if (ret != 0) {
			PRT_ERR("set mirror failed ret: %d.\n", ret);
			return ret;
		}
	} else if (cc_si->mirror) {
		PRT_DBG("cc_si->mirror\n");
		ret = cc_si->write_reg(cc_si, 0x17, 0x15);
		if (ret != 0) {
			PRT_ERR("set mirror failed ret: %d.\n", ret);
			return ret;
		}
	}

	return 0;

}

static int sensor_set_stream(struct cc_sensor_info *cc_si, int on)
{
	int ret = 0;

	return ret;
}

/*
 * ctrl for sensor start
 */
static int gc2145_set_ae_target(struct cc_sensor_info *cc_si, int val)
{
	int ret = 0;

	ret += cc_si->write_reg(cc_si, 0xfe, 0x1);
	ret += cc_si->write_reg(cc_si, 0x13, val);
	ret += cc_si->write_reg(cc_si, 0x15, val);

	ret += cc_si->write_reg(cc_si, 0x18, 0xf7);
	ret += cc_si->write_reg(cc_si, 0x19, 0xf7);

	PRT_DBG("target: 0x%x\n", val);
	return ret;
}

static long gc2145_set_ae_meter(struct cc_sensor_info *cc_si, void *arg)
{
	struct viss_isp_ae_para *ae_para = (struct viss_isp_ae_para *)arg;
	int pix = 4;
	long ret = 0;

	/*
	 * PRT_ERR("VIDIOC_VISS_MIPI_SET_AE_METER %d, %d, %d, %d, %d.\n",
	 *	ae_para->rect.left,
	 *	ae_para->rect.top, ae_para->rect.width,
	 *	ae_para->rect.height, ae_para->ae_mode);
	 */
	if (cc_si->cur_frmcfg->width == 1600) {
		PRT_INFO("win size is 1600x1200\n");
		if (ae_para->ae_mode ==
			V4L2_EXPOSURE_METERING_CENTER_WEIGHTED) {

			if ((ae_para->rect.width < 2 * 8 * pix) ||
					(ae_para->rect.height < 2 * 8 * pix))
				return ret;

			if (ae_para->rect.left < 8 * pix)
				ae_para->rect.left = 8 * pix;

			if (ae_para->rect.top < 8 * pix)
				ae_para->rect.top = 8 * pix;

			if (ae_para->rect.left +
				ae_para->rect.width >= (1600 - 8 * pix))
				ae_para->rect.width =
					1600 - 8 * pix - ae_para->rect.left;

			if (ae_para->rect.top +
				ae_para->rect.height >= (1200 - 8 * pix))
				ae_para->rect.height =
					1200 - 8 * pix - ae_para->rect.top;

			/*
			 * PRT_ERR("VIDIOC_VISS_MIPI_SET_AE_METER %d,
			 *	%d, %d, %d.\n",
			 *	ae_para->rect.left, ae_para->rect.top,
			 *	ae_para->rect.width, ae_para->rect.height);
			 */

			ret += cc_si->write_reg(cc_si, 0xfe, 0x1);
			ret += cc_si->write_reg(cc_si, 0x01,
				ae_para->rect.left / 8 - pix);
			ret += cc_si->write_reg(cc_si, 0x03,
				ae_para->rect.top / 8 - pix);
			ret += cc_si->write_reg(cc_si, 0x02,
				(ae_para->rect.left + ae_para->rect.width)
				/ 8 + pix);
			ret += cc_si->write_reg(cc_si, 0x04,
				(ae_para->rect.top + ae_para->rect.height)
				/ 8 + pix);
			ret += cc_si->write_reg(cc_si, 0x05,
				ae_para->rect.left / 8);
			ret += cc_si->write_reg(cc_si, 0x07,
				ae_para->rect.top / 8);
			ret += cc_si->write_reg(cc_si, 0x06,
				(ae_para->rect.left + ae_para->rect.width)
				/ 8);
			ret += cc_si->write_reg(cc_si, 0x08,
				(ae_para->rect.top + ae_para->rect.height)
				/ 8);
		} else {
			ret += cc_si->write_reg(cc_si, 0xfe, 0x1);
			ret += cc_si->write_reg(cc_si, 0x01, 0x04);
			ret += cc_si->write_reg(cc_si, 0x02, 0xc0);
			ret += cc_si->write_reg(cc_si, 0x03, 0x04);
			ret += cc_si->write_reg(cc_si, 0x04, 0x90);
			ret += cc_si->write_reg(cc_si, 0x05, 0x24);
			ret += cc_si->write_reg(cc_si, 0x06, 0x80);
			ret += cc_si->write_reg(cc_si, 0x07, 0x1b);
			ret += cc_si->write_reg(cc_si, 0x08, 0x60);
		}
	} else {
		if (ae_para->ae_mode ==
			V4L2_EXPOSURE_METERING_CENTER_WEIGHTED) {

			if ((ae_para->rect.width < 2 * 8 * pix) ||
					(ae_para->rect.height < 2 * 8 * pix))
				return ret;

			if (ae_para->rect.left < 8 * pix)
				ae_para->rect.left = 8 * pix;

			if (ae_para->rect.top < 8 * pix)
				ae_para->rect.top = 8 * pix;

			if (ae_para->rect.left +
				ae_para->rect.width >= (800 - 8 * pix))
				ae_para->rect.width =
					800 - 8 * pix - ae_para->rect.left;

			if (ae_para->rect.top +
				ae_para->rect.height >= (600 - 8 * pix))
				ae_para->rect.height =
					600 - 8 * pix - ae_para->rect.top;

			/*
			 * PRT_ERR("VIDIOC_VISS_MIPI_SET_AE_METER %d,
			 *	%d, %d, %d.\n",
			 *	ae_para->rect.left, ae_para->rect.top,
			 *	ae_para->rect.width, ae_para->rect.height);
			 */

			ret += cc_si->write_reg(cc_si, 0xfe, 0x1);
			ret += cc_si->write_reg(cc_si, 0x01,
				ae_para->rect.left / 8 - pix);
			ret += cc_si->write_reg(cc_si, 0x03,
				ae_para->rect.top / 8 - pix);
			ret += cc_si->write_reg(cc_si, 0x02,
				(ae_para->rect.left + ae_para->rect.width)
				/ 8 + pix);
			ret += cc_si->write_reg(cc_si, 0x04,
				(ae_para->rect.top + ae_para->rect.height)
				/ 8 + pix);
			ret += cc_si->write_reg(cc_si, 0x05,
				ae_para->rect.left / 8);
			ret += cc_si->write_reg(cc_si, 0x07,
				ae_para->rect.top / 8);
			ret += cc_si->write_reg(cc_si, 0x06,
				(ae_para->rect.left + ae_para->rect.width)
				/ 8);
			ret += cc_si->write_reg(cc_si, 0x08,
				(ae_para->rect.top + ae_para->rect.height)
				/ 8);
		} else {
			ret += cc_si->write_reg(cc_si, 0xfe, 0x1);
			ret += cc_si->write_reg(cc_si, 0x01, 0x04);
			ret += cc_si->write_reg(cc_si, 0x02, 0x60);
			ret += cc_si->write_reg(cc_si, 0x03, 0x02);
			ret += cc_si->write_reg(cc_si, 0x04, 0x48);
			ret += cc_si->write_reg(cc_si, 0x05, 0x18);
			ret += cc_si->write_reg(cc_si, 0x06, 0x50);
			ret += cc_si->write_reg(cc_si, 0x07, 0x10);
			ret += cc_si->write_reg(cc_si, 0x08, 0x38);
		}
	}

	return ret;
}

static int gc2145_get_exif(struct cc_sensor_info *cc_si,
			struct viss_isp_exif *exif)
{
	int ret = 0;
	u8 tmp;
	u16 exp_line = 0;

	ret += cc_si->write_reg(cc_si, 0xfe, 0x1);

	ret = cc_si->read_reg(cc_si, 0x14, &tmp);/* average lum */
	exif->res[2] = tmp;

	ret = cc_si->read_reg(cc_si, 0x13, &tmp);/* Y target */
	exif->res[4] = tmp;

	ret += cc_si->write_reg(cc_si, 0xfe, 0x0);
	ret = cc_si->read_reg(cc_si, 0xb1, &tmp);/* pre_gain */
	exif->iso_speed = tmp * 100 / 16;

	ret = cc_si->read_reg(cc_si, 0x03, &tmp);/* exposure */
	exp_line = tmp;

	ret = cc_si->read_reg(cc_si, 0x04, &tmp);/* exposure */
	exp_line = (exp_line << 8) | tmp;

	exif->exposure_time = exp_line * 700 / 1750;/* 10000 / 14.277fps */

	return ret;
}

/*
 * V4L2 subdev internal operations
 */
static long sensor_ioctl(struct cc_sensor_info *cc_si, unsigned int cmd,
			void *arg)
{
	long ret = 0;

	switch (cmd) {
	case VIDIOC_ISP_SET_AE_METER:
		ret = gc2145_set_ae_meter(cc_si, arg);
		break;
	case VIDIOC_ISP_SET_AE_TARGET:
		gc2145_set_ae_target(cc_si, *(unsigned int *) arg);
		break;
	case VIDIOC_VISS_ISP_EXIF_REQ:
		gc2145_get_exif(cc_si, (struct viss_isp_exif *)arg);
		break;
	default:
		return -EINVAL;
	}
	return ret;
}

/*
 * ctrl for sensor finish
 */


#define DRIVER_NAME "gc2145_mipi"

static int sensor_probe(struct platform_device *pdev)
{
	struct cc_sensor_info *cc_si = NULL;
	int ret = 0;

	PRT_INFO("*********probe begin\n");

	cc_si = devm_kzalloc(&pdev->dev, sizeof(*cc_si), GFP_KERNEL);
	if (cc_si == NULL) {
		PRT_ERR("Can't alloc memory!\n");
		return -ENOMEM;
	}

	cc_si->reg_bits = 8;
	cc_si->val_bits = 8;

	cc_si->name = DRIVER_NAME;

	cc_si->power_up_act = power_up_act;
	cc_si->power_down_act = power_down_act;
	cc_si->npwr_up_acts = ARRAY_SIZE(power_up_act);
	cc_si->npwr_dn_acts = ARRAY_SIZE(power_down_act);

	cc_si->framecfgs = frame_cfgs;
	cc_si->nfrmcfgs = ARRAY_SIZE(frame_cfgs);

	ret = cc_sensor_probe(pdev, cc_si);
	if (ret < 0)
		return ret;

	cc_si->detect = sensor_detect;
	cc_si->sensor_init = sensor_init;
	cc_si->set_stream = sensor_set_stream;
	cc_si->sensor_ioctl = sensor_ioctl;

	PRT_INFO("probe end\n");
	return 0;
}

static int sensor_remove(struct platform_device *pdev)
{
	struct cc_sensor_info *cc_si = platform_get_drvdata(pdev);

	cc_sensor_remove(pdev);

	devm_kfree(&pdev->dev, cc_si);

	return 0;
}

static const struct of_device_id gc2145_mipi_of_match[] = {
	{
		.compatible = DRIVER_NAME,
	},
	{ /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, gc2145_mipi_of_match);

static struct platform_driver gc2145_mipi_driver = {
	.driver = {
		.of_match_table	= gc2145_mipi_of_match,
		.name		= DRIVER_NAME,
		.owner		= THIS_MODULE,
	},
	.probe = sensor_probe,
	.remove = sensor_remove,
};

module_platform_driver(gc2145_mipi_driver);

MODULE_DESCRIPTION("GC2145-MIPI sensor driver");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:" DRIVER_NAME);

